package SM::Server;

use warnings;
use strict;

use POE qw(Component::Server::TCP Filter::Stream Session);
use Module::Load;
use Data::Dumper::Simple;
use File::Basename;
use Socket qw(IPPROTO_TCP TCP_NODELAY);

use SM::Server::Client;
use SM::Constants;
use SM::Server::Chat;

# Variable Declarations
our %clients;
our $global_poe_kernel = $poe_kernel;

# Recursively load all Packet:: modules
# Get a list of modules that we need loaded
my $modules = load_modules("SM::Server::Packet");

# Start them
foreach my $module (@$modules) {
	load $module;
}

# Create Chat server
our $global_chat_server = SM::Server::Chat->new();

# Create TCP server on port 9500
POE::Component::Server::TCP->new(
	Alias => "sm_server",
	Port  => 9500,
	InlineStates =>
		{ send => \&handle_send, name_client => \&handle_name_client, },
	ClientConnected    => \&client_connected,
	ClientError        => \&client_error,
	ClientDisconnected => \&client_disconnected,
	ClientInput        => \&client_input,
	ClientFilter       => POE::Filter::Stream->new(),
);

# Start the POE kernel
$poe_kernel->run();
exit 0;

#################################
# TCP Server POE Event Handlers #
#################################

# Handle an outgoing message by sending it to the client.
sub handle_send {
	my ( $heap, $data ) = @_[ HEAP, ARG0 ];
	#print Dumper($data);
	$heap->{client}->put($data);
}

sub handle_name_client {
	my ($alias) = $_[ARG0];
	$poe_kernel->alias_set($alias);
}

# Create the SM::Server::Client for the new Client
sub client_connected {
	my $session_id = $_[SESSION]->ID;
	my $heap       = $_[HEAP];
	# Disable Nagle's Algorithm
	my $socket = $heap->{client}->[0];	
	setsockopt($socket, IPPROTO_TCP, TCP_NODELAY, 1);
}

# The client disconnected.  Remove them from the chat room and
# broadcast a message to whoever is left.
sub client_disconnected {
	my $session_id = $_[SESSION]->ID;
	# Send a packet saying the dude is gone!

}

# The client socket has had an error.  Remove them from the chat room
# and broadcast a message to whoever is left.
sub client_error {
	my $session_id = $_[SESSION]->ID;
	my ($username) = $poe_kernel->alias_list($session_id);
	delete $clients{$username};
}

# Send the incoming packet to be decoded
sub client_input {
	my ( $kernel, $session, $input ) = @_[ KERNEL, SESSION, ARG0 ];
	my $session_id = $session->ID;

	my ($username) = $poe_kernel->alias_list($session_id);

	my $packet = SM::Server::Packet->decode_packet(
		{ raw_data => $input, client => $session_id, username => $username } );
	#print Dumper($packet);
	if ( defined($packet) ) {
		$packet->dispatch();
	}
}

#################################
# IRC Server POE Event Handlers #
#################################

##############################
# Internal Utility Functions #
##############################

# Dynamic module loader
sub load_modules {
	my @modules_to_load = @_;
	my @modules;
	my @module_paths;

	my @already_done;

	foreach my $module_name (@modules_to_load) {
		push @already_done, ($module_name);
		foreach my $inc_path (@INC) {

			my $path    = $module_name;
			my @counter = split /::/, $path;
			my $count   = scalar @counter;

			$path =~ s/::/\//g;

			my $full_path = $inc_path . "/" . $path . "/";
			if ( -e $full_path ) {
				foreach my $module ( glob "$full_path*.pm" ) {

					# print $module_name;
					#require $module;
					my $full_module_name
						= $module_name . "::" . ( fileparse( $module, ".pm" ) );
					push @modules_to_load, ($full_module_name);
				}

			}

		}
	}
	push @modules, @already_done;
	return \@modules;
}

###############################################
# Utility Functions  for use by other modules #
###############################################

# Send a packet
sub send_packet {
	my ( $class, $recipient, $packet ) = @_;
	
	if (my $session_reference = $poe_kernel->alias_resolve( $recipient )) {
		$poe_kernel->post( $session_reference => send => $packet );
	}
	else {
		$poe_kernel->post( $recipient => send => $packet );
	}
	
	return 0;
}

# Set an alias for a session
sub set_client_alias {
	my ( $class, $client, $alias ) = @_;
	$poe_kernel->post( $client => name_client => $alias )

}

=head1 NAME

SM::Server - The great new SM::Server!

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

=head1 SYNOPSIS

Quick summary of what the module does.

Perhaps a little code snippet.

    use SM::Server;

    my $foo = SM::Server->new();
    ...

=head1 EXPORT

A list of functions that can be exported.  You can delete this section
if you don't export anything, such as for a purely object-oriented module.

=head1 FUNCTIONS

=head2 function1

=cut

=head2 function2

=cut

=head1 AUTHOR

Casey Banner, C<< <kcbanner at gmail.com> >>

=head1 BUGS

Please report any bugs or feature requests to
C<bug-sm-server at rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=SM-Server>.
I will be notified, and then you'll automatically be notified of progress on
your bug as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc SM::Server

You can also look for information at:

=over 4

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/SM-Server>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/SM-Server>

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=SM-Server>

=item * Search CPAN

L<http://search.cpan.org/dist/SM-Server>

=back

=head1 ACKNOWLEDGEMENTS

=head1 COPYRIGHT & LICENSE

Copyright 2007 Casey Banner, all rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut

1;    # End of SM::Server
